﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Informaiton_Retrieval_Hw3.data;
using System.Windows.Forms;
using Informaiton_Retrieval_Hw3.UI;
using ServiceRanking;

namespace Informaiton_Retrieval_Hw3
{
    enum Category
    {
        earn,
        acq,
        moneyfx,
        grain,
        crude,
        trade,
        interest,
        ship, 
        wheat, 
        corn
    };

    class Model
    {
        private MainForm _form;
        private List<ReutersData> RDList = new List<ReutersData>();
        private Dictionary<Category, List<int>> tempDictionary = new Dictionary<Category, List<int>>();
        public Dictionary<Category, int[]> docDictionary = new Dictionary<Category, int[]>();
        public Dictionary<Category, float[]> docVectorDictionary = new Dictionary<Category, float[]>();
        private string[] docs;
        TFIDFMeasure tf;
        LoadFileForm loadForm;

        public float[] earnVector, acqVector, moneyfxVector, grainVector, crudeVector, tradeVector, interestVector, shipVector, wheatVector, cornVector;

        public int TrainDocs { get { return trainDocCount; } }

        int trainDocCount;

        public Model(MainForm form)
        {
            this._form = form;
            InitDictionary();
            docs = new string[0];
            loadForm = new LoadFileForm();
            StopWordsHandler stopword = new StopWordsHandler();
        }

        private void InitDictionary()
        {
            tempDictionary.Add(Category.acq, new List<int>());
            tempDictionary.Add(Category.corn, new List<int>());
            tempDictionary.Add(Category.crude, new List<int>());
            tempDictionary.Add(Category.earn, new List<int>());
            tempDictionary.Add(Category.grain, new List<int>());
            tempDictionary.Add(Category.interest, new List<int>());
            tempDictionary.Add(Category.moneyfx, new List<int>());
            tempDictionary.Add(Category.ship, new List<int>());
            tempDictionary.Add(Category.trade, new List<int>());
            tempDictionary.Add(Category.wheat, new List<int>());
        }
       
        public void InitData()
        {
            trainDocCount = RDList.Count;
            docs = getDocs(RDList);
            RDList.Clear();
            CovertListToArray();
            tf = new TFIDFMeasure();
            tf.OnProgressEvent += pUpdate;
            tf.OnProgressStart += pStart;
            tf.OnProgressEnd += pEnd;
            tf.Init(docs);
            CalculationClassVector();
        }

        void pUpdate(float total, float count)
        {
            float r = (count + 1) / total * 100f;
            loadForm.Progress = (int)r;
        }

        void pStart(string msg) {
            loadForm.Show();
            //loadForm.Location = new System.Drawing.Point();
            loadForm.Text = msg;
        }

        void pEnd(string msg)
        {
            loadForm.Progress = 100;
            loadForm.Hide();
        }


        public Category ClassifyTestData(string[] str)
        {
            float df,idf;
            float[] testVector = new float[tf._numTerms];
            TFIDFMeasure testTF = new TFIDFMeasure();
            testTF.Init(str);
            for (int i = 0; i < testTF._numTerms; i++)
            {
                df = 0;
                idf = 0;
                df = testTF.GetTermFrequency(i, 0);//df 第i個term的Frequency
                int termIndex = tf.GetTermIndex((string)testTF.GetTerms()[i]);
                if (termIndex == -1)//不存在
                {
                }
                else
                {
                    idf = tf.GetInverseDocumentFrequency(termIndex);
                    testVector[termIndex] = df * idf;
                }
            }

            Category category = Category.acq;
            float maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, acqVector);

            if(maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, cornVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, cornVector);
                category = Category.corn;
            }

            if(maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, crudeVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, crudeVector);
                category = Category.crude;
            }

            if(maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, earnVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, earnVector);
                category = Category.earn;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, grainVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, grainVector);
                category = Category.grain;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, interestVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, interestVector);
                category = Category.interest;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, moneyfxVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, moneyfxVector);
                category = Category.moneyfx;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, shipVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, shipVector);
                category = Category.ship;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, tradeVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, tradeVector);
                category = Category.trade;
            }

            if (maxValue < ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, wheatVector))
            {
                maxValue = ServiceRanking.TFIDFMeasure.TermVector.ComputeCosineSimilarity(testVector, wheatVector);
                category = Category.wheat;
            }

            return category;
        }

        public void CalculationClassVector() 
        {
            acqVector = CalculationVector(Category.acq);
            cornVector = CalculationVector(Category.corn);
            crudeVector = CalculationVector(Category.crude);
            earnVector = CalculationVector(Category.earn);
            grainVector = CalculationVector(Category.grain);
            interestVector = CalculationVector(Category.interest);
            moneyfxVector = CalculationVector(Category.moneyfx);
            shipVector = CalculationVector(Category.ship);
            tradeVector = CalculationVector(Category.trade);
            wheatVector = CalculationVector(Category.wheat);
        }

        public float[] CalculationVector(Category category)
        {
            pStart("CalculationVector: "+category.ToString());
            int docCount = docDictionary[category].Length;  //這個分類有幾個文件
            int fakeDocCount = docCount;
            float[] totalvector = new float[tf._numTerms];
            for (int i = 0; i < docCount; i++)
            {
                pUpdate(docCount, i);
                if (docDictionary[category][i] >= MaxDoc)
                {
                    fakeDocCount--;
                }
                else
                {
                    float[] v = tf.GetTermVector(docDictionary[category][i]);//第i個文件 那現在要處理的地方是超過MAX 不看他 數量要扣掉
                    totalvector = AddFloatArray(totalvector, v);
                }
            }
            pEnd("");
            return DivideFloatArray(totalvector, fakeDocCount);
        }

        private int getProgerss(float total, float count)
        {
            float r = (count + 1) / total * 100f;
            return (int)r;
        }

        private float[] AddFloatArray(float[] a, float[] b) 
        {
            float[] vector;
            vector = a;
            for (int i = 0; i < a.Length; i++) 
            {
                vector[i] += b[i];
            }
            return vector;
        }

        private float[] DivideFloatArray(float[] a, int b)
        {
            float[] vector;
            vector = a;
            for (int i = 0; i < a.Length; i++)
            {
                vector[i] = vector[i] / (float)b;
            }
            return vector;
        }

        int MaxDoc = 4000;

        private string[] getDocs(List<ReutersData> rDList)
        {
            int Max = rDList.Count;
            if (Max > MaxDoc)
                Max = MaxDoc;
            string[] docs = new string[Max];
            for (int i = 0; i < docs.Length; i++)
            {
                string body = rDList[i].Body;
                docs[i] = body;
            }
            return docs;
        }

        public void load(string filePath)
        {
            using (StreamReader sr = new StreamReader(filePath, Encoding.UTF8))
            {
                List<string> termList = new List<string>();
                string line;
                string ReutersInfo = "";
                bool reutersStartFlag = false;
                while ((line = sr.ReadLine()) != null)
                {
                    if (line.Contains("<REUTERS") || reutersStartFlag) reutersStartFlag = true;
                    if (reutersStartFlag) ReutersInfo += line + " ";
                    if (line.Contains("</REUTERS>")) reutersStartFlag = false;
                    if (!reutersStartFlag && ReutersInfo != "")
                    {
                        //MessageBox.Show(ReutersInfo);
                        ReutersData rd = new ReutersData(ReutersInfo);
                        if (rd.LewisSplit == "TRAIN" && rd.HasTopic && rd.isTopic) //
                        {
                            if (rd.Body.Length != 0)
                            {
                                RDList.Add(rd);
                                if (rd.isTopicContent("acq")) tempDictionary[Category.acq].Add(RDList.Count - 1);
                                if (rd.isTopicContent("corn")) tempDictionary[Category.corn].Add(RDList.Count - 1);
                                if (rd.isTopicContent("crude")) tempDictionary[Category.crude].Add(RDList.Count - 1);
                                if (rd.isTopicContent("earn")) tempDictionary[Category.earn].Add(RDList.Count - 1);
                                if (rd.isTopicContent("grain")) tempDictionary[Category.grain].Add(RDList.Count - 1);
                                if (rd.isTopicContent("interest")) tempDictionary[Category.interest].Add(RDList.Count - 1);
                                if (rd.isTopicContent("money-fx")) tempDictionary[Category.moneyfx].Add(RDList.Count - 1);
                                if (rd.isTopicContent("ship")) tempDictionary[Category.ship].Add(RDList.Count - 1);
                                if (rd.isTopicContent("trade")) tempDictionary[Category.trade].Add(RDList.Count - 1);
                                if (rd.isTopicContent("wheat")) tempDictionary[Category.wheat].Add(RDList.Count - 1);
                            }
                        }
                        ReutersInfo = "";
                    }
                }
            }
        }

        private void CovertListToArray()
        {
            foreach (Category c in tempDictionary.Keys)
            {
                docDictionary.Add(c, tempDictionary[c].ToArray());
            }
            tempDictionary.Clear();
        }
    }
}
